//
//  ARUploadUtil.swift
//  AquaRaft
//
//  Created by 何启亮 on 2024/4/18.
//

import Foundation
import Alamofire

class ARUploadUtilHelper {
    
    private init() {
        // Do ntohing...
    }
    
    class func uploadJpg(_ data: Data,
                               progressHandler: ((Progress) -> Void)? = nil,
                               completion: @escaping (ARRequestResult<String>) -> Void) -> ARUploadUtil<ARUploadResponseModel>
    {
        return Self.uploadFile(.data(data: data, type: .jpg), progressHandler: progressHandler) { result in
            switch result {
            case .successWithoutResponse:
                completion(.faild(err: .modelConvertError(error: nil)))
            case .faild(let err):
                completion(.faild(err: err))
            case .success(let model):
                completion(.success(model: model.filename))
            }
        }
    }
    
    private class func uploadFile<convertModel: Decodable>(_ file: ARUploadUtil<convertModel>.UploadFile,
                                  progressHandler: ((Progress) -> Void)? = nil,
                                  completion: @escaping (ARRequestResult<convertModel>) -> Void) -> ARUploadUtil<convertModel>
    {
        let userId = ARUserManager.shared.userId ?? "unknown"
        let uploadUtil = ARUploadUtil<convertModel>.init(uploadFile: file, userId: userId, progressHandler: progressHandler) { result in
            completion(result)
        }
        let _ = uploadUtil.start()
        return uploadUtil
    }
    
}

/// 上传
/// - note:
///  上传流程:
///     1. 从 /user/oss/policy 接口获取相关的上传配置
///     2. 上传，参数由 1 的接口提供
///     3. 转换path
class ARUploadUtil<Model: Decodable> {
    
    /// 上传文件
    enum UploadFile {
        case data(data: Data, type: UploadFileType)
        case file(filePath: String, type: UploadFileType)
        
        var fileType: UploadFileType {
            switch self {
            case .data(_, let type):
                return type
            case .file(_, let type):
                return type
            }
        }
    }
    
    /// 文件类型
    enum UploadFileType: String {
        case jpg = "jpg"
        case png = "png"
        
        var mimeType: String {
            switch self {
            case .jpg:
                return "image/jpg"
            case .png:
                return "image/png"
            }
        }
    }
    
    // MARK: - Property
    
    /// 上传进度回调
    var progressHandler: ((Progress) -> Void)?
    /// 回调
    var completion: (ARRequestResult<Model>) -> Void
    /// Userid
    var userId: String
    
    /// 上传的文件
    let uploadFile: UploadFile
//    let uploadFileType: UploadFileType
    
    // MARK: Private
    
    private var ossRequest: ARFetchUploadHostRequest?
    private var uploadRequest: UploadRequest?
    private var isCancelled: Bool = false
    
    // MARK: - Init
    
    /// 创建一个上传任务
    init(uploadFile: UploadFile,
         userId: String,
         progressHandler: ((Progress) -> Void)? = nil,
         completion: @escaping (ARRequestResult<Model>) -> Void)
    {
        self.progressHandler = progressHandler
        self.completion = completion
        self.uploadFile = uploadFile
        self.userId = userId
    }
    
    // MARK: - Function
    
    /// 开始
    func start() -> Bool {
        if isCancelled {
            // 已取消
            return false
        }
        
        // 先获取数据
        if let ossRequest = ossRequest {
            ossRequest.cancel()
            self.ossRequest = nil
        }
        
        ossRequest = ARFetchUploadHostRequest()
        ossRequest?.fetchUploadConfig { [weak self] result in
            if self?.isCancelled ?? false {
                return
            }
            self?.ossRequest = nil
            
            switch result {
            case .faild(let err):
                self?.completion(.faild(err: err))
            case .success(let model):
                // 下一步 -> 上传
                self?.upload(model)
            case .successWithoutResponse:
                self?.completion(.faild(err: ARRequestError.normalError))
            }
        }
        
        return true
    }
    
    func cancel() {
        if let ossRequest = ossRequest {
            ossRequest.cancel()
            self.ossRequest = nil
        }
        if let uploadRequest = uploadRequest, uploadRequest.isCancelled == false {
            uploadRequest.cancel()
            self.uploadRequest = nil
        }
    }
    
    private func upload(_ model: AROssPolicyModel) {
        if isCancelled {
            return
        }
        guard let url = URL(string: model.host) else {
            // 有问题
            completion(.faild(err: ARRequestError.normalError))
            return
        }
        // 上传
        if let uploadRequest = uploadRequest, uploadRequest.isCancelled == false {
            uploadRequest.cancel()
            self.uploadRequest = nil
        }
        let fileName = "\(Int(Date().timeIntervalSince1970)).\(uploadFile.fileType.rawValue)"
        let uploadPath = "\(model.dir)\(userId)/"+fileName
        let params = [
            "ossaccessKeyId" : model.accessKeyId,
            "policy" : model.policy,
            "signature" : model.signature,
            "callback" : model.callback,
            "key": uploadPath
        ]
        let uploadFile = self.uploadFile
        uploadRequest = AF.upload(multipartFormData: { formData in
            
            for (key, value) in params {
                formData.append(value.data(using: .utf8)!, withName: key)
            }
            
            switch uploadFile {
            case .data(let data, let type):
                formData.append(data, withName: "file", fileName: fileName, mimeType: type.mimeType)
            case .file(let filePath, let type):
                let fileUrl = URL(fileURLWithPath: filePath)
                formData.append(fileUrl, withName: "file", fileName: fileName, mimeType: type.mimeType)
            }
            
        }, to: url, method: .post, headers: HTTPHeaders(ARRequestHelper.commonHeaders()))
        uploadRequest?.responseDecodable(of: ARResponseModel.self) { [weak self] dataResponse in
            guard let self = self else {
                return
            }
            if self.isCancelled {
                return
            }
            self.uploadRequest = nil
            switch dataResponse.result {
            case .failure(let error):
                self.completion(.faild(err: .serviceError(error: error)))
                return
            case .success(let responseModel):
                if responseModel.code != 0 {
                    self.completion(.faild(err: .apiError(code: responseModel.code, msg: responseModel.msg ?? "Api error")))
                    return
                }
                // 直接解码
                guard let data = responseModel.data else {
                    // 直接失败
                    self.completion(.faild(err: ARRequestError.modelConvertError(error: nil)))
                    return
                }
                
                do {
                    let encoder = JSONEncoder()
                    let jsonData: Data
                    switch data {
                    case .array(let jsonArray):
                        jsonData = try encoder.encode(jsonArray)
                        break
                    case .dictionary(let json):
                        jsonData = try encoder.encode(json)
                        break
                    default:
                        // 转换失败
                        self.completion(.faild(err: .modelConvertError(error: nil)))
                        return
                    }
                    
                    // 转码
                    let decoder = JSONDecoder()
                    let model = try decoder.decode(Model.self, from: jsonData)
                    self.completion(.success(model: model))
                } catch {
                    // 转换失败
                    self.completion(.faild(err: .modelConvertError(error: error)))
                }
                return
            }
        }
    }
}

// MARK: - Upload response

class ARUploadResponseModel: Decodable {
    let filename: String
    let height: String
    let mimeType: String
    let size: String
    let width: String
}

// MARK: -

/*
 "data": {
   "accessKeyId": "LTAI5tNPNT6g1inCHkxgcUGz",
   "policy": "eyJleHBpcmF0aW9uIjoiMjAyNC0wNC0yNVQxMzo1MDoxMy41ODRaIiwiY29uZGl0aW9ucyI6W1siY29udGVudC1sZW5ndGgtcmFuZ2UiLDAsMjA5NzE1MjAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsImxpdmNoYXQvaW1hZ2VzL2Rldi8yMDI0MDQxOC8iXV19",
   "signature": "pzj8SQXXTPDL4k9TgEms/TGFrww=",
   "expire": "1714053013584",
   "dir": "livchat/images/dev/20240418/",
   "host": "https://livchat.oss-us-west-1.aliyuncs.com",
   "callback": "eyJjYWxsYmFja0JvZHkiOiJmaWxlbmFtZT0ke29iamVjdH0mc2l6ZT0ke3NpemV9Jm1pbWVUeXBlPSR7bWltZVR5cGV9JmhlaWdodD0ke2ltYWdlSW5mby5oZWlnaHR9JndpZHRoPSR7aW1hZ2VJbmZvLndpZHRofSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQiLCJjYWxsYmFja1VybCI6Imh0dHA6Ly90ZXN0LWFwcC5pb2h1Ym9ubGluZS5jbHViL2NhbGxiYWNrL29zcy9jYWxsYmFjayJ9"
 }
 */

fileprivate class AROssPolicyModel: Decodable {
    
    let accessKeyId: String
    let callback: String
    let dir: String
    let expire: String
    let host: String
    let policy: String
    let signature: String
    
}

/// 获取上传host
fileprivate class ARFetchUploadHostRequest: ARRequest {
    
    override var api: String {
        "/user/oss/policy"
    }
    
    func fetchUploadConfig(_ completion: @escaping (_ result: ARRequestResult<AROssPolicyModel>) -> Void) {
        self.request(of: AROssPolicyModel.self) { response, result in
            completion(result)
        }
    }
    
}

/// 转换路径
class ARMediaConvertPathRequest: ARRequest {
    
    private var encodingArrayParams : [Any]?
    
    override var api: String {
//        "/shortLink/media/get"
        "/shortLink/media/search"
    }
    
    override var httpMethod: HTTPMethod {
        .post
    }
    
    override var encoder: ParameterEncoding {
        if let encodingArrayParams = encodingArrayParams {
            return ArrayEncoding(array: encodingArrayParams)
        }
        return JSONEncoding.default
    }
    
//    func convertPhotoPathToUrl(_ filePath: String, completion: @escaping (ARRequestResult<String>) -> Void) {
//        self.parameters = [
//            "mediaPath" : filePath,
//            "mediaType" : "photo"
//        ]
//        self.request { response, result in
//            switch result {
//            case . successWithoutResponse:
//                completion(.faild(err: ARRequestError.modelConvertError(error: nil)))
//            case .faild(let err):
//                completion(.faild(err: err))
//            case .success(let model):
//                // 解析
//                guard case let .dictionary(dict) = model,
//                      case let .string(avatarUrl) = dict["mediaUrl"] else {
//                    completion(.faild(err: ARRequestError.modelConvertError(error: nil)))
//                    return
//                }
//                completion(.success(model: avatarUrl))
//            }
//        }
//    }
    
    func convertVideoPathsToUrls(_ filePaths: [String], completion: @escaping (ARRequestResult<[String]>) -> Void) {
        var list: [[String: String]] = []
        for path in filePaths {
            list.append([
                "mediaPath": path,
                "mediaType" : "video"
            ])
        }
        self.encodingArrayParams = list
        
        self.request { [weak self] response, result in
            self?.encodingArrayParams = nil
            switch result {
            case . successWithoutResponse:
                completion(.faild(err: ARRequestError.modelConvertError(error: nil)))
            case .faild(let err):
                completion(.faild(err: err))
            case .success(let model):
                // 解析
                guard case let .array(list) = model else {
                    completion(.faild(err: ARRequestError.modelConvertError(error: nil)))
                    return
                }
                var urls: [String] = []
                for decode in list {
                    guard case let .dictionary(dict) = decode,
                          case let .string(videoUrl) = dict["mediaUrl"] else {
                        completion(.faild(err: ARRequestError.modelConvertError(error: nil)))
                        return
                    }
                    urls.append(videoUrl)
                }
                completion(.success(model: urls))
            }
        }
    }
    
}
